package excelrept

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

//FormulaCalc 公式计算
type FormulaCalc struct {
	reptName        string
	tConf           *Table
	calc            *ExpCalc
	cellData        map[string]string
	cellformulas    map[string]string
	formulaCellVals map[string]float64
}

//NewFormulaCalc 创建公式计算
func NewFormulaCalc(reptName string, tConf *Table, cellData map[string]string) (f *FormulaCalc, err error) {
	tpl, err := GetTemplate(reptName)
	if err != nil {
		return nil, err
	}
	ti := tpl.getTableItem(tConf.Name)
	ti.DataCells = cellData
	calc := NewExpCalc(ti)
	f = &FormulaCalc{
		reptName:        reptName,
		tConf:           tConf,
		cellData:        cellData,
		calc:            calc,
		cellformulas:    tpl.getTableItem(tConf.Name).cellformulas,
		formulaCellVals: make(map[string]float64, 20),
	}
	return
}

//CalcFormulas 计算公式并返回结果
func (f *FormulaCalc) CalcFormulas(ignoreFms ...string) (formulaCellVals map[string]float64, err error) {
	var val float64
	var isIgnore bool
	for cell, exp := range f.cellformulas {
		isIgnore = false
		if exp == "" {
			err = fmt.Errorf("单元格%s的计算表达式为空，配置错误?", cell)
			return
		}
		for _, fm := range ignoreFms {
			if cell == fm {
				isIgnore = true
				break
			}
		}
		if isIgnore {
			continue
		}
		val, err = f.calcFormula(exp)
		if err != nil {
			return nil, err
		}
		f.formulaCellVals[cell] = val
	}
	formulaCellVals = f.formulaCellVals
	return
}

func (f *FormulaCalc) calcFormula(exp string, ignoreFms ...string) (val float64, err error) {
	cells, opers, err := f.parseExp(exp)
	if err != nil {
		return 0, err
	}
	var v float64
	var ok bool
	var exp2 string
	var cellVal string
	var dv time.Time
	var col int
	for i, cell := range cells {
		v, ok = f.formulaCellVals[cell]
		if !ok {
			exp2, ok = f.cellformulas[cell]
			for _, fm := range ignoreFms {
				if cell == fm {
					ok = false
					break
				}
			}
			if ok {
				v, err = f.calcFormula(exp2)
			} else {
				cellVal = f.cellData[cell]
				if cellVal == "" {
					cellVal = "0"
				}
				col, _, err = parseAxis(cell)
				if err != nil {
					return 0, err
				}
				if f.calc.tableItem.ColumnTypes[col] == "date" {
					if cellVal == "" {
						v = 0
					} else {
						dv, err = time.Parse("2006/01/02", cellVal)
						if err != nil {
							err = fmt.Errorf("单元格%s日期格式正确，正确的格式为2020/01/09", cell)
							return
						}
						v = float64(dv.Unix())
					}
				} else {
					v, err = strconv.ParseFloat(cellVal, 64)
				}
				if err != nil {
					return 0, err
				}
			}
		}
		if opers[i] == '+' {
			val += v
		} else if opers[i] == '-' {
			val -= v
		} else if opers[i] == '*' {
			val *= v
		} else if opers[i] == '/' {
			if v == 0 {
				val = 0
			} else {
				val /= v
			}
		} else {
			err = fmt.Errorf("不支持的运算符%b", opers[i])
		}
	}
	if f.calc.tableItem.ColumnTypes[col] == "date" {
		val = val / 3600 / 24
	}
	return
}
func (f *FormulaCalc) parseExp(exp string) (cellExps []string, opers []byte, err error) {
	if exp == "" {
		return
	}
	sm := map[int]string{1: "字母", 2: "数字", 3: "+,-,*,/", 4: "SUM", 5: "(", 6: ")", 7: ",", 8: ":"} //状态标识
	sc := map[int][]int{0: {1, 5}, 1: {1, 2}, 2: {2, 3, 6}, 3: {1, 4}, 4: {5}, 5: {6}, 6: {3}}
	stat := 0
	st := sc[0]
	opers = make([]byte, 0, 10)
	opers = append(opers, '+')
	cellExps = make([]string, 0, 2)
	expBytes := make([]byte, 0, 10)
	sumBytes := make([]byte, 0, 10)
	var isOk bool
	i := 0
	isSum := false
	if strings.ToUpper(string(exp[0:3])) == "SUM" {
		isSum = true
		stat = 4
		st = sc[stat]
		i = 3
	}
	for ; i < len(exp); i++ {
		b := exp[i]
		isOk = false
		if stat == 5 && isSum && b != ')' {
			sumBytes = append(sumBytes, b)
			continue
		}
		if stat == 3 && i+3 < len(exp) && strings.ToUpper(string(exp[i:i+3])) == "SUM" {
			isSum = true
			stat = 4
		}
		for _, s := range st {
			if s == 1 && isLetter(b) {
				stat = 1
				st = sc[1]
				isOk = true
				break
			} else if s == 2 && isNumber(b) {
				stat = 2
				st = sc[2]
				isOk = true
				break
			} else if s == 3 && isOper(b) {
				stat = 3
				st = sc[3]
				isOk = true
				break
			} else if s == 4 {
				stat = 4
				st = sc[4]
				isOk = true
				i += 3
				break
			} else if s == 5 && b == '(' {
				stat = 5
				st = sc[5]
				isOk = true
				break
			} else if s == 6 && b == ')' {
				stat = 6
				st = sc[6]
				isOk = true
				break
			}
		}
		if !isOk {
			msg := ""
			for i, s := range st {
				msg += sm[s]
				if i < len(st)-1 {
					msg += "或者"
				}
			}
			err = fmt.Errorf("错误的表达式%s，表达式的第%d位必须是%s", exp, i, msg)
			return
		}
		if stat == 1 || stat == 2 {
			expBytes = append(expBytes, b)
		} else if stat == 3 {
			if len(expBytes) > 0 {
				cellExps = append(cellExps, string(expBytes))
				opers = append(opers, b)
				expBytes = make([]byte, 0, 10)
			}
		} else if stat == 6 && isSum {
			if len(sumBytes) > 0 {
				cells, _, err := f.calc.parseRange("01", string(sumBytes))
				if err != nil {
					return nil, nil, err
				}
				for _, cell := range cells {
					cellExps = append(cellExps, cell)
					opers = append(opers, '+')
				}
				opers = append(opers, b)
				sumBytes = make([]byte, 0, 10)
				isSum = false
			} else {
				err = fmt.Errorf("错误的表达式%s，SUM的表达式为空", exp)
				return
			}
		}
	}
	if stat == 5 || stat == 3 {
		err = fmt.Errorf("错误的表达式%s，表达式不完整", exp)
		return
	}
	if len(expBytes) > 0 {
		cellExps = append(cellExps, string(expBytes))
	}
	return
}
